import pydantic

from typing import Any

from .sqlalchemy import *


# ----------------------------------------------------------
#
#                          BaseModel
#
# ----------------------------------------------------------


class BaseModel(pydantic.BaseModel):
    def dict_data(self, exclude_none=False):
        data = {}

        for k, v in self.dict(exclude_none=exclude_none).items():
            if isinstance(v, str):
                v = v.strip()

            data[k] = v

        return data

    @classmethod
    def from_obj(cls, obj, excludes=None):
        if isinstance(obj, Base):
            model = cls.parse_obj(obj.dict(excludes=excludes))
        elif isinstance(obj, pydantic.BaseModel):
            model = cls.parse_obj(obj.dict())
        else:
            model = cls.parse_obj(obj)

        return model


# ----------------------------------------------------------
#
#                         扩展类型模型
#
# ----------------------------------------------------------


class ChoiceModel(BaseModel):
    code: int
    value: str

    @classmethod
    def from_obj(cls, obj):
        if isinstance(obj, Choice):
            model = cls(code=obj.code, value=obj.value)
        elif isinstance(obj, (tuple, list)):
            model = cls(code=obj[0], value=obj[1])
        else:
            model = cls.parse_obj(obj)

        return model


CoordinateModel = tuple[float, float]


# ----------------------------------------------------------
#
#                           扩展模型
#
# ----------------------------------------------------------


class ListPermissionModel(BaseModel):
    add: bool = False
    delete: bool = False


class PermissionModel(BaseModel):
    update: bool = False
    delete: bool = False

    data: dict | None


class PaginationArgsModel(BaseModel):
    page: int
    limit: int

    def _offset(self):
        if self.page <= 0:
            self.page = 1

        return (self.page - 1) * self._limit()

    def _limit(self):
        if self.limit <= 0:
            self.limit = 10

        return self.limit

    def query(self, session, stmt):
        if stmt._order_by_clauses:
            return session.execute(stmt.limit(self._limit()).offset(self._offset()))
        else:
            return session.execute(
                stmt.limit(self._limit())
                .offset(self._offset())
                .order_by(stmt.selectable.selected_columns.id.desc())
            )


class PaginationModel(BaseModel):
    count: int = 0
    num_pages: int = 0
    data: list[Any] = []

    permission: ListPermissionModel = ListPermissionModel()

    def init(self, session, stmt, limit):
        self.count = session.count(stmt)
        self.num_pages = self.count // limit

        if self.count % limit > 0:
            self.num_pages += 1

        self.data = []


class ListModel(BaseModel):
    count: int = 0
    data: list[Any] = []

    permission: ListPermissionModel = ListPermissionModel()

    def update(self):
        self.count = len(self.data)


class NameModel(BaseModel):
    id: int
    name: str


class UserNameModel(NameModel):
    phone: str


class SuccessModel(BaseModel):
    success: bool = True
    data: dict | None


class ConfirmModel(BaseModel):
    ok: bool = True
    description: str | None


class FileModel(BaseModel):
    file: str


class UseModel(BaseModel):
    use: bool = True


class CoordinateAddressModel(BaseModel):
    coordinate: CoordinateModel
    address: str | None


class StatisticModel(BaseModel):
    id: int | None
    name: str
    count: int = 0
    percent: str | None

    more: bool = True


class StatisticListModel(BaseModel):
    name: str | None
    count: int = 0
    data: list[StatisticModel] = []

    def update(self):
        for x in self.data:
            self.count += x.count


class MenuModel(BaseModel):
    name: str
    label: str


class LoginArgsModel(BaseModel):
    phone: str
    password: str | None
    code: str | None


class LoginModel(BaseModel):
    token: str


class PasswordUpdateModel(BaseModel):
    password: str
    code: str


class BackgroundTaskModel(BaseModel):
    id: int
    name: str
    count: int
    finished: int
    success: bool | None
    data: dict | None

    rate: float = 0

    def update(self, obj):
        if self.success:
            self.rate = 1
        else:
            if obj.count > 0 and obj.finished > 0:
                self.rate = min(round(obj.finished / obj.count, 4), 0.99)
